/**
 * Crazy Table Edit
 *  License by CrazyYi.
 *  v1.0.0 20200206
 *  v1.1.0 20200212: 增加支持时间格式转换
 *  v1.1.1 20200214: 增加支持枚举类型
 *  v1.2.0 20200218: 增加edit属性，支持单元格不允许编辑
 *  v1.3.0 20200228: 增加reload 属性，定义表格是否重新加载（注意：http的success优先级最高，会覆盖）
 *  v1.4.0 20200426: 增加editLimit，如果页面显示的limit超过这个数，则关闭单元格编辑操作
 *
 *  reference: http://yuntao.xyz:5555/guide/table-demo/editable.html
 */
const editModules = [
    'table', 'jquery', 'form',
    'crazyTool', 'crazyQuery', 'crazyData', 'crazyHttp', 'crazyBox', 'crazyIcon', 'crazyVerify',
];
layui.define(editModules, function (exports) {
    const MOD_NAME = 'crazyEdit'
    const MOD_MODE = 'edit'
    const TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
    const $ = layui.$,
        $Tool = layui.crazyTool,
        $Box = layui.crazyBox,
        $Icon = layui.crazyIcon,
        $tQuery = layui.crazyQuery,
        $Data = layui.crazyData,
        $Http = layui.crazyHttp,
        $Verify = layui.crazyVerify,
        $Date = layui.crazyDate

    const defaultOptions = {
        "v": '1.0.0',
        mode: 'local',  // 过滤模式，local, api
        logOn: false, // 是否开启 log 打印
        done: function (config) {
        },

        config: [],    // 配置项，一维数组
    };
    /**
     * 保存本类中最后的 Options
     * @type {{}}
     */
    let gOptions = {};

    const gConfig = {
        type: 'input',      // input, checkbox, radio，select
        format: undefined,
        edit: true, // 默认单元格支持 编辑，v1.2

        mode: 'local',      // 数据请求方式, local：默认, api：http参数详见httpConfig.update

        field: undefined,   // 字段
        name: undefined,    // 用于api请求用的name，如果不存在，则和field一样

        dict: [],   // 当checkbox，radio的情况下，如果是local模式，需要用到。否则就采用url请求获取
        arrs: [],   // 保存当前table对应列的所有取值（去重）

        verify: undefined,  // 验证规则，详见lay-verify
        verType: 'tips',    // 异常提示层模式，tips（吸附层），alert（对话框），msg（默认）
        reqText: undefined, // 用于自定义必填项（即设定了 lay-verify="required" 的表单）的提示文本 layui2.5.0新增
        placeholder: undefined, // 支持自定义。如果没有定义，则由系统根据规则默认生成对应字符串

        httpType: undefined, // 对应 httpConfig 的CURD
        http: {}, // 暂时不使用
        before: function (opt) {return opt.where}
    };
    let gConfigs = [];

    let gRes = {};
    let gData = [];
    /**
     * 保存主表的 options信息：在done回调中获取到
     * @type {{}}
     */
    let gMainTable = {};

    /**
     * 定义ajax 请求 CURD 相关参数
     */
    let gHttp = {};
    // v1.3.0，单独设置如下参数
    const defaultSet = {
        pkey: {field: 'id', name: 'id'}, // v1.1.0，单独保存,主表的索引字段field，以及对应http请求的名称name
        mark: {
            strConn: ',', // 字符串 拼接字符（说明是数组），暂不支持修改
            arrConn: ',', // 转换成HTTP请求的数组拼接字符
            rangeConn: '~', // 范围拼接字符，说明该字段是范围，比如1~8
        }, // 数据参数字符
    };
    let gSet = {};
    let gPKey = {};

    //========================================================
    // 执行
    //========================================================

    /**
     * 类初始化
     * @type {{data: data, options: options, http: http, render: render, config: config, table: layui.table}}
     */
    const classInit = {
        render: function (myTable, res) {
            gMainTable = myTable;
            gRes = res;
            gData = res.data;

            // 1.加载 预处理模块：crazyQuery, crazyData, crazyHttp
            this.http();
            this.data();    // 保存所有数据
            this.table();   // 保存 table 信息
            this.setting(); // 保存 setting

            // 2.设置 本项目配置
            this.options();
            this.config();
        },
        config: function () {
            if (!!gMainTable.editLimit && gMainTable.limit > gMainTable.editLimit) {
                return; // 20200426：如果每页显示的数量超过20，则不进行单元格编辑处理
            }
            let cols = gMainTable.cols[0];
            $.each(cols, function (i, col) {
                if (col.hasOwnProperty('modify') && col.hasOwnProperty('field') && !col.hasOwnProperty('templet')) {
                    let isNew = true;
                    // 先从已有的configs中查找是否已经添加了该field
                    $.each(gConfigs, function (j, config) {
                        if (config.hasOwnProperty('field') && config.field === col.field) {
                            isNew = false;
                            return false; // break，找到了，说明不是新增的，不添加
                        }
                    });

                    if (!isNew) {
                        return true;  // continue，退出当前循环，进行下一个循环
                    }
                    // 走到这里，说明是新增的field，需要添加
                    let newC = gConfig.deepClone(); // 注意：这里不能直接=gConfig，也不能用$.extend，因为对象是指针，会导致循环中所有的newC 都指向同一个gConfig
                    $.extend(true, newC, col.modify);
                    newC.field = col.field;
                    newC.name = newC.name || (col.name || col.field);
                    newC.placeholder = $Verify.placeholder(newC.verify, col.modify.placeholder);
                    newC.format = newC.format || (newC.type === 'datetime' ? TIME_FORMAT : undefined);

                    // 保存 http 配置
                    if (!!newC.httpType) {
                        let refHttp = gHttp[newC.httpType];
                        if (!!refHttp && Object.keys(refHttp).length > 0) {
                            let newHttp = {};
                            $.extend(true, newHttp, refHttp);
                            $.extend(true, newHttp, newC.http);
                            newC.http = newHttp;
                        }
                    }
                    // 设置 datetime format
                    if (newC.type === 'datetime') {
                        let fmt = newC.format || {};
                        fmt.req = fmt.req || TIME_FORMAT;
                        fmt.show = fmt.show || TIME_FORMAT;
                        newC.format = fmt;
                    }
                    // 增加reload属性，v1.3.0
                    if (!!newC.reload && !newC.http.success) {
                        newC.http.success = function (res) {$tQuery.reload()}
                    }

                    gConfigs.push(newC);       // 将edit config 保存
                }
            });
            log.print('init configs', gConfigs);
        },
        options: function () {
            // 先获取所有 配置
            $.extend(gOptions, defaultOptions);
            $.extend(true, gOptions, gMainTable.filterConfig);
        },
        http: function () {
            gHttp = $Http.options();
            Object.keys(gHttp).length || (gMainTable.httpConfig && $.extend(gHttp, gMainTable.httpConfig) && $Http.render(gHttp));
        },
        data: function () {
            ($Data.getCount && typeof $Data.getCount === 'function') && ($Data.getCount() || $Data.render(gMainTable, gRes));
        },
        table: function () {
            !!$tQuery.tbElem().length || $tQuery.render(gMainTable.elem)
        },
        setting: function () {
            $.extend(true, gSet, defaultSet, gMainTable.setting); // v1.3.0，独立设置信息
            $.extend(gPKey, gSet.pkey);
        }, // 保存setting，v1.3.0
    };

    /**
     * 类方法
     * @type {{main: main, render: render}}
     */
    const classIns = {
        /**
         * 类加载
         * @param myTable
         * @param res
         */
        render: function (myTable, res) {
            console.log('--------- crazyTable Edit ------------------');
            classInit.render(myTable, res);

            this.main();
        },
        main: function () {
            if (!!gMainTable.editLimit && gMainTable.limit > gMainTable.editLimit) {
                console.log('main table', gMainTable)
                return; // 20200426：如果每页显示的数量超过20，则不进行单元格编辑处理
            }

            let othis = this;
            let clickToShow = function (e, options, config) {
                e.stopPropagation();
                options.config.arrs = $Data.getColArray(config.field); // 获取当前列的值（去重）
                options.logOn = gOptions.logOn || false;
                $Box.render(options); // 弹出层加载
            };

            let trs = $tQuery.getTrs(); // 得到所有的tr
            let btnId = '';
            $.each(gConfigs, function (key, config) {
                let field = config.field;

                $.each(trs, function (iRow, tr) {
                    let index = $(tr).data('index'); // 获取每一行的序号，LAY_TABLE_INDEX（这个是layui自带的参数），下标从0开始
                    let td = $tQuery.getTd(field, index);// 获取field列中第index行（即单元格）
                    let value = td.find('div').text(); // 获取当前单元格的内容
                    let pId = gData[index][gPKey.field]; // 获取到当前行记录的索引值 primary key

                    let _html = singleCell(config, index, value, pId); // 重构单元格的html
                    $tQuery.getTdCell(td).html(_html); // 该单元格内容更新
                    btnId = gName.btnSubmit(field, index); // submit 按钮的名称

                    // 如果当前单元格支持编辑，v1.2
                    if (!!config.edit) {
                        // 在cell 中添加icon
                        let $_icon = $Icon.render(config, MOD_MODE); // 图标设置
                        $tQuery.getTdCell(td).append($_icon);

                        // 监听event 双击单元格，出现“弹出层”
                        const options = {
                            cell: td,
                            config: config,
                            mode: MOD_MODE,
                            value: value,
                            newBtns: [],
                            submit: submit,
                            closed: undefined,
                            logOn: gOptions.logOn,
                        };
                        td.on('dblclick', function (e) {
                            clickToShow(e, options, config); // 弹出层加载
                        });
                        $_icon.on("click", function (e) {
                            clickToShow(e, options, config); // 弹出层加载
                        });
                    }

                });

            });

            $(document).keydown(function (e) {
                if (e.keyCode === 13) {
                    log.print('keydown press 13');
                    $("#" + btnId).trigger("click");
                }
            });
        },

    };

    /**
     * 生成 单元格edit 的html
     * @param config
     * @param index
     * @param value
     * @param pId
     * @returns {string}
     */
    const singleCell = function (config, index, value, pId) {
        // 不采用templet方式更新，而是采用直接修改html方式覆盖单元格
        let field = config.field;
        let placeholder = config.placeholder; // $Verify.placeholder(modify.verify, modify.placeholder);
        let verify = (config.verify);
        let btnId = gName.btnSubmit(field, index); // submit 按钮的名称
        let classV = $Verify.class(index);
        let curV = value;

        // 如果是时间格式，则需要进行转换
        if (config.type === 'datetime') {
            let fmt = config.format;
            let curD = (fmt.req === 'int') ? new Date(value * 1000) : new Date(value);
            curV = $Date.formatTime(curD, fmt.show); // 显示的值
            // let curK = $Date.formatTime(curD, fmt.req); // 传递的值
        }

        // 如果有dict，说明这个是枚举类型v1.1.1
        if (config.dict.length > 0) {
            if (config.type === 'select' || config.type === 'checkbox' || config.type === 'radio') {
                if (config.field === config.name) {
                    // 说明当前传递的是dict 的key，我们需要提取对应的value 显示
                    $.each(config.dict, function (i, item) {
                        if (item.key + '' === curV + '') {
                            curV = item.value;
                            return false;
                        }
                    })
                } else {
                    // 说明当前传递的是dict 的value，则不需要做任何操作
                }
            }
        }

        // 将需要edit 的单元格，修改成 input 编辑框（只读），双击后才能显示编辑框
        let _input = `<input type="text" data-name="${field}" value="${curV}" readonly lay-filter="${btnId}" autocomplete="off"
                          data-index="${index}" lay-verify="${verify}" lay-verType="tips" data-pkid="${pId}"
                          class="layui-input layui-input-inline ${classV}" placeholder="${placeholder}"/>`;
        let _btn = `<button id="${btnId}" class="layui-btn layui-hide" lay-submit lay-filter="${btnId}"></button>`;
        let _html = `<form class="layui-form layui-inline crazy-layui-input" lay-filter="${btnId}">${_input}${_btn}</form>`;
        return _html;
    };
    const submit = function (opt) {
        let boxValue = opt.values;
        let config = opt.config;
        let cell = opt.cell;
        let row = opt.row;

        let where = {};
        where[config.name] = boxValue.req;

        // 更新当前单元格内容
        cell.find('input').val(boxValue.show);
        $Icon.has(); // 设置 高亮

        where[gPKey.name] = cell.find('input').data('pkid'); // 获取当前记录的主索引值

        // before处理
        let tr = $(cell).parent('tr');
        let param = {row: row, cell: cell, values: boxValue, tr: tr, where: where};
        config.before && typeof config.before === 'function' && (where = config.before(param));
        log.print('http update where', where);
        if (where === false) { return } // 如果where===false，表示后续的操作要中止。这样可以避免某些不必要的提交

        // 对于编辑来说，既要更新本地缓存，也要更新服务器
        // 更新本地缓存（api 和 local 都需要更新本地）
        $Data.update(where);

        // 更新服务器
        if (config.mode === 'api') {
            $.extend(config.http.where, {}, where);
            // config.http.success = function(res){
            //     // $tQuery.reload({"page": {"curr": 1}}); // 开启api搜索的时候，自动回到第一页
            // };
            $Http.httpReq(config.http, true);
        }

    };

    /**
     * 全局需要用到的各种名称
     * @type {{btnSubmit: (function(*, *): string)}}
     */
    let gName = {
        btnSubmit: function (field, index) { return `queryCondBtn_${field}_${index}`; },
    };

    const log = {
        /**
         * 打印Log
         * @returns {boolean}
         */
        print: function () {
            let logOn = gOptions.logOn || false;
            if (!logOn) return false;
            const tips = 'crazyTable Edit';
            console.log(tips + ' | ', arguments[0], arguments[1] || '',
                arguments[2] || '', arguments[3] || '', arguments[4] || '', arguments[5] || '');
            return true;
        },
    };

    /**
     * 供外部调用的成员
     *
     * @type {{render: (function(*=): layerTable)}}
     */
    const gExports = {
        render: function (myTable, data) {
            return classIns.render(myTable, data);
        },
        config: function () {
            return gOptions;
        },
    };

    //输出接口
    exports(MOD_NAME, gExports);

});